import { Injectable, InternalServerErrorException } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import * as bcrypt from 'bcryptjs'
import { DataSource, FindOneOptions, In, Repository } from 'typeorm'
import { AuthUserDto } from '@/auth/dto/auth-user.dto'
import { PAGENATION } from '@/constants/pagenation.constants'
import { Role } from '@/entities/role.entity'
import { User } from '@/entities/user.entity'
import { UserRole } from '@/entities/user-role.entity'
// import { conditionUtils } from '@/utils/db.helper'
import { CreateUserDto } from './dto/create-user.dto'
import { GetUserDto } from './dto/get-user.dto'
import { UpdateUserDto } from './dto/update-user.dto'
@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    @InjectRepository(Role)
    private readonly roleRepository: Repository<Role>,
    @InjectRepository(UserRole)
    private readonly userRoleRepository: Repository<UserRole>,
    private dataSource: DataSource
  ) {}

  async create(createUserDto: CreateUserDto, operatorUser?: User) {
    // 判断要创建的用户账户是否存在
    const createUser = await this.findOne({ userName: createUserDto.userName })
    if (createUser) {
      // 用户账户存在
      throw new InternalServerErrorException('用户已存在')
    }

    // 创建用户
    const userTemp = this.userRepository.create(createUserDto)
    const userRoles = []
    // 判断用户角色是否存在
    if (createUserDto?.userRoles?.length) {
      const roles = await this.roleRepository.findBy({
        roleCode: In(createUserDto.userRoles),
      })
      if (!roles.length) {
        throw new InternalServerErrorException('角色不存在')
      }
      for (const role of roles) {
        const userRole = new UserRole()
        if (operatorUser) {
          userRole.createId = operatorUser.id
          userRole.createBy = operatorUser.userName
        }
        userRole.role = role
        userRoles.push(this.userRoleRepository.create(userRole))
      }
    }
    if (userRoles?.length) {
      userTemp.roles = userRoles
    }

    if (createUserDto.password) {
      /**
       * 加密处理 - 异步方法
       * bcrypt.hash(data, salt)
       *    - data  要加密的数据
       *    - slat  用于哈希密码的盐。如果指定为数字，则将使用指定的轮数生成盐并将其使用。推荐 10
       */
      userTemp.password = await bcrypt.hash(createUserDto.password, 10)
    } else {
      userTemp.password = await bcrypt.hash('123456', 10)
    }
    return this.userRepository.save(userTemp)
  }

  async register(authUserDto: AuthUserDto, operatorUser?: User) {
    // 判断要创建的用户账户是否存在
    const createUser = await this.findOne({ userName: authUserDto.userName })
    if (createUser) {
      // 用户账户存在
      throw new InternalServerErrorException('用户已存在')
    }

    // 创建用户
    const userTemp = this.userRepository.create(authUserDto)
    if (operatorUser) {
      userTemp.updateBy = operatorUser.userName
      userTemp.updateId = operatorUser.id
    }

    /**
     * 加密处理 - 异步方法
     * bcrypt.hash(data, salt)
     *    - data  要加密的数据
     *    - slat  用于哈希密码的盐。如果指定为数字，则将使用指定的轮数生成盐并将其使用。推荐 10
     */
    userTemp.password = await bcrypt.hash(userTemp.password, 10)
    return this.userRepository.save(userTemp)
  }

  async verfiyAutorify(id: string, permissions: string[]) {
    const user = await this.userRepository.findOne({
      where: { id },
      relations: ['roles', 'roles.role', 'roles.role.permissions'],
    })
    // 判断用户是否存在
    if (!user) {
      throw new InternalServerErrorException('用户不存在')
    }

    // 判断用户是否被禁用
    if (user.status !== '1') {
      throw new InternalServerErrorException('用户已经被禁用')
    }
    const userPermissions = user.roles.flatMap((userRole) =>
      userRole.role.permissions.map((permission) => permission.permissionCode)
    )
    console.log('userPermissions', userPermissions)
    const hasPermission = permissions.every((item) => userPermissions.includes(item))
    if (!hasPermission) {
      throw new InternalServerErrorException('权限不足')
    }

    return true
  }

  async findAll(query: GetUserDto) {
    // page - 页码，limit - 每页条数，condition-查询条件(userName, role, sex)，sort-排序
    const { current = PAGENATION.current, size = PAGENATION.size, ...rest } = query
    const take = size // 条数
    const skip = (current - 1) * take // 页码 - 要跳过多少条

    // 查询参数
    // const obj = {
    //   'user.userName': userName,
    // }

    // const queryBuilder = this.userRepository
    //   .createQueryBuilder('user')
    //   .leftJoinAndSelect('user.roles', 'user-role')
    //   .leftJoinAndSelect('user-role.role', 'role.roleCode')
    // const newQueryBuilder = conditionUtils<User>(queryBuilder, obj)

    // // return this.userRepository.find({
    // //   take,
    // //   skip,
    // //   where: {
    // //     userName,
    // //   },
    // // });

    // const [records, total] = await newQueryBuilder.take(take).skip(skip).getManyAndCount()
    const [records, total] = await this.userRepository.findAndCount({
      relations: ['roles', 'roles.role'],
      where: rest,
      take,
      skip,
    })
    const result = records.map((item) => {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { password, ...rest } = item
      return rest
    })
    return {
      records: result,
      total,
      current,
      size,
    }
  }

  findOne(condition: Partial<User>) {
    return this.userRepository.findOne({
      relations: ['roles', 'roles.role', 'roles.role.permissions'],
      where: condition,
    })
  }

  findOneById(id: string, options: FindOneOptions<User> = null) {
    const params: FindOneOptions<User> = {
      where: { id },
    }
    if (options) {
      for (const key in options) {
        params[key] = options[key]
      }
    }
    return this.userRepository.findOne(params)
  }

  async update(id: string, updateUserDto: UpdateUserDto, operatorUser?: PayloadUserType) {
    const { roles: userRole, ...restUser } = await this.userRepository.findOne({
      where: { id },
      relations: ['roles'],
    })
    const { userRoles, ...rest } = updateUserDto
    const params: Omit<User, 'roles'> = {
      ...restUser,
      ...rest,
      updateBy: operatorUser?.userName || null,
      updateId: operatorUser?.userId || null,
    }

    if (updateUserDto.password) {
      params.password = await bcrypt.hash(updateUserDto.password, 10)
    }
    const queryRunner = this.dataSource.createQueryRunner()
    try {
      await queryRunner.connect()
      await queryRunner.startTransaction()
      console.log('params', params)
      await queryRunner.manager.save(this.userRepository.create(params))
      const roles = await this.roleRepository.findBy({
        roleCode: In(userRoles),
      })
      // 判断用户角色是否存在
      if (userRoles.length && !roles.length) {
        throw new InternalServerErrorException('角色不存在')
      }
      const deleteIds = userRole
        .filter(
          (userRoleItem) => !roles.map((roleItem) => roleItem.id).includes(userRoleItem.roleId)
        )
        .map((mapItem) => mapItem.id)
      console.log('deleteIds', deleteIds)
      if (deleteIds.length) {
        await queryRunner.manager.delete(UserRole, deleteIds)
      }
      const insertRoles = roles.filter(
        (roleItem) => !userRole.map((userRoleItem) => userRoleItem.roleId).includes(roleItem.id)
      )
      console.log('insertRoles', insertRoles)
      if (insertRoles.length) {
        const userRolesEntity = []
        for (const role of insertRoles) {
          const insertUserRole = new UserRole()
          if (operatorUser) {
            insertUserRole.createId = operatorUser.userId
            insertUserRole.createBy = operatorUser.userName
          }
          insertUserRole.userId = restUser.id
          insertUserRole.role = role
          userRolesEntity.push(this.userRoleRepository.create(insertUserRole))
        }
        await queryRunner.manager.insert(UserRole, userRolesEntity)
      }
      await queryRunner.commitTransaction()
      return '操作成功'
    } catch (error) {
      throw new InternalServerErrorException(error)
    } finally {
      await queryRunner.release()
    }
  }

  async remove(ids: number[]) {
    try {
      await this.userRepository.delete(ids)
      return `删除成功`
    } catch (error) {
      return error
    }
  }
}
